#include "stdafx.hpp"
#include "ColligereAudioDatabaseHandler.hpp"
#include "ColligereUtils.hpp"
#include "ColligereDatabaseError.hpp"

//////////////////////////////////////////////////////////
// ColligereAudioDatabaseEvent
//////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS ( col::AudioDatabaseEvent, col::DatabaseEventBase );

//////////////////////////////////////////////////////////
// ColligereAudioDatabaseHandler
//////////////////////////////////////////////////////////

col::AudioDatabaseHandler::AudioDatabaseHandler()
{
	static bool tables_initialized = false;
	if ( !tables_initialized )
	{
		typedef DatabaseTable::Column c;
		typedef c::Type ct;
		typedef c::Constraint cc;

		typedef DatabaseTable::Trigger tr;
		typedef tr::EventType evt_t;
		typedef tr::ExecPosition exec_p;

		DatabaseTable artists ( "artists", DatabaseTypeId::AudioDatabase );

		typedef Tables::Artists a_tb;

		artists += c ( a_tb::Name, "name", ct::Text, cc::NotNull, false, 0 );
		artists += c ( a_tb::Country, "country", ct::Text, 0, false, -1 );
		artists += c ( a_tb::Homepage, "homepage", ct::Text, 0, false, -1 );

		artists += tr ( "clean_albums", evt_t::Delete, exec_p::After, 
			"DELETE FROM albums WHERE parent_artist_id = OLD.id;", "artists" );

		DatabaseTableListHandler::Get().RegisterTable ( artists );

		DatabaseTable albums ( "albums", DatabaseTypeId::AudioDatabase, artists.GetName() );

		typedef Tables::Albums albums_tb;

		albums += c ( albums_tb::ParentArtistId, "parent_artist_id", ct::Integer, cc::NotNull, true );
		albums += c ( albums_tb::Title, "title", ct::Text, cc::NotNull, false, -1 );
		albums += c ( albums_tb::Year, "year", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Genre, "genre", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Format, "format", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Condition, "condition", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::IsTrashed, "is_trashed", ct::Integer );
		albums += c ( albums_tb::TimeStampAdded, "timestamp_added", ct::Text );
		albums += c ( albums_tb::TimeStampUpdated, "timestamp_updated", ct::Text );
		albums += c ( albums_tb::Status, "status", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::ExtInfo, "ext_info", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Label, "label", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::CatalogNum, "catalog_num", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Rating, "rating", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::Tags, "tags", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::PurchasePrice, "purchase_price", ct::Text, 0, false, -1 );
		albums += c ( albums_tb::SellPrice, "sell_price", ct::Text, 0, false, -1 );

		albums += tr ( "clean_tracks", evt_t::Delete, exec_p::After, 
			"DELETE FROM tracks WHERE parent_album_id = OLD.id;", "albums" );

		DatabaseTableListHandler::Get().RegisterTable ( albums );

		DatabaseTable tracks ( "tracks", DatabaseTypeId::AudioDatabase, albums.GetName() );

		typedef Tables::Tracks tracks_tb;

		tracks += c ( tracks_tb::ParentAlbumId, "parent_album_id", 
			ct::Integer, cc::NotNull, true );
		tracks += c ( tracks_tb::Num, "num", ct::Text, 0, false, 0 );
		tracks += c ( tracks_tb::Title, "title", ct::Text, cc::NotNull, false, -1 );
		tracks += c ( tracks_tb::Length, "length", ct::Text, 0, false, -1 );

		DatabaseTableListHandler::Get().RegisterTable ( tracks );

		tables_initialized = true;
	}

	ConnectEventHandlers();
}

col::AudioDatabaseHandler::~AudioDatabaseHandler()
{
}

void col::AudioDatabaseHandler::ConnectEventHandlers()
{
	Connect ( mId, COLLIGERE_EVT_DATABASE_ITEM_REQUEST, 
		ColligereAudioDatabaseEventHandlerFunction
		( &AudioDatabaseHandler::OnDatabaseItemRequest ) );
}

const col::DatabaseItemBaseDeque
col::AudioDatabaseHandler::Search ( const wxString &rTerm, wxUint32 flags ) const
{
	using namespace std;

	DatabaseItemBaseDeque ret;

	if ( flags & SearchFlag::UseRegEx )
	{
		const wxRegEx re ( rTerm, wxRE_ADVANCED );

		foreach ( const DatabaseItemBasePtr artist, mItemList )
		{
			// If the artist has the column value, then add all albums from that artist.
			if ( artist->HasColumnValue ( re ) )
			{
				ret.insert ( ret.begin(), artist->GetChildren().begin(),
					artist->GetChildren().end() );
				continue;
			}
			foreach ( const DatabaseItemBasePtr album, artist->GetChildren() )
			{
				if ( album->HasColumnValue ( re ) )
				{
					ret.push_back ( album );
					continue;
				}
				foreach ( const DatabaseItemBasePtr track, album->GetChildren() )
				{
					// If the track has the column value, then add the parent album.
					if ( track->HasColumnValue ( re ) )
					{
						ret.push_back ( album );
						break;
					}
				}
			}
		}
	}
	else
	{
		const bool case_sensitive = ( flags & SearchFlag::CaseSensitive ) == 0;

		foreach ( const DatabaseItemBasePtr artist, mItemList )
		{
			// If the artist has the column value, then add all albums from that artist.
			if ( artist->HasColumnValue ( rTerm, case_sensitive ) )
			{
				ret.insert ( ret.begin(), artist->GetChildren().begin(),
					artist->GetChildren().end() );
				continue;
			}
			foreach ( const DatabaseItemBasePtr album, artist->GetChildren() )
			{
				if ( album->HasColumnValue ( rTerm, case_sensitive ) )
				{
					ret.push_back ( album );
					continue;
				}
				foreach ( const DatabaseItemBasePtr track, album->GetChildren() )
				{
					// If the track has the column value, then add the parent album.
					if ( track->HasColumnValue ( rTerm, case_sensitive ) )
					{
						ret.push_back ( album );
						break;
					}
				}
			}
		}
	}
	return ret;
}

const col::DatabaseItemBaseDeque
col::AudioDatabaseHandler::SearchFromColumn 
( const wxString &rTerm, bool caseSensitive, const DatabaseTable &rTable,
 const DatabaseTable::Column &rColumn ) const
{
	using namespace boost;
	using namespace std;

	typedef const DatabaseItemBasePtr item_ptr;

	DatabaseItemBaseDeque ret;

	const wxString term ( caseSensitive ? rTerm : rTerm.Lower() );

	if ( rTable.GetName() == "artists" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			const wxString col_val ( ( caseSensitive ? (*artist)[rColumn.GetIndex()] : 
				( (*artist)[rColumn.GetIndex()].Lower() ) ) );

			if ( artist->HasColumnValue ( term, caseSensitive ) )
			{
				ret.insert ( ret.begin(), artist->GetChildren().begin(),
					artist->GetChildren().end() );
			}
		}
	}
	else if ( rTable.GetName() == "albums" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			foreach ( item_ptr album, artist->GetChildren() )
			{
				const wxString col_val ( ( caseSensitive ? (*album)[rColumn.GetIndex()] : 
					(*album)[rColumn.GetIndex()].Lower() ) );

				if ( col_val == term )
				{
					ret.push_back ( album );
				}
			}
		}
	}
	else if ( rTable.GetName() == "tracks" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			foreach ( item_ptr album, artist->GetChildren() )
			{
				foreach ( item_ptr track, album->GetChildren() )
				{
					const wxString col_val ( ( caseSensitive ? (*track)[rColumn.GetIndex()] : 
						(*track)[rColumn.GetIndex()].Lower() ) );

					if ( col_val == term )
					{
						ret.push_back ( album );
						break;
					}
				}
			}
		}
	}
	else
	{
		wxFAIL_MSG ( "Invalid table" );
	}
	return ret;
}

const col::DatabaseItemBaseDeque col::AudioDatabaseHandler::SearchFromColumn 
 ( const wxRegEx &rRe, const DatabaseTable &rTable,
 const DatabaseTable::Column &rColumn ) const
{
	using namespace boost;
	using namespace std;

	typedef const DatabaseItemBasePtr item_ptr;

	DatabaseItemBaseDeque ret;

	if ( rTable.GetName() == "artists" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			if ( rRe.Matches ( (*artist)[rColumn.GetIndex()] ) )
			{
				copy ( artist->GetChildren().begin(),
					artist->GetChildren().end(), ret.begin() );
			}
		}
	}
	else if ( rTable.GetName() == "albums" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			foreach ( item_ptr album, artist->GetChildren() )
			{
				if ( rRe.Matches ( (*album)[rColumn.GetIndex()] ) )
				{
					ret.push_back ( album );
				}
			}
		}
	}
	else if ( rTable.GetName() == "tracks" )
	{
		foreach ( item_ptr artist, mItemList )
		{
			foreach ( item_ptr album, artist->GetChildren() )
			{
				foreach ( item_ptr &track, album->GetChildren() )
				{
					if ( rRe.Matches ( (*track)[rColumn.GetIndex()] ) )
					{
						ret.push_back ( album );
						break;
					}
				}
			}
		}
	}
	else
	{
		wxFAIL_MSG ( "Invalid table" );
	}
	return ret;
}


wxUint32 col::AudioDatabaseHandler::GetTypeId() const
{
	return DatabaseTypeId::AudioDatabase;
}

void col::AudioDatabaseHandler::DeleteItem ( const DatabaseItemBase &rItem )
{
	if ( dynamic_cast < const Album * > ( &rItem ) )
	{
		Artist *artist = FindItem ( rItem.GetParentId() );
		Album *album = artist->FindChild ( rItem.GetId() );
		wxASSERT ( album );

		artist->GetState() += DatabaseItemState::Flag::ChildDeleted;
		album->GetState() += DatabaseItemState::Flag::Deleted;

		if ( artist->GetChildCount() == 1 )
		{
			artist->GetState() = DatabaseItemState::Flag::Deleted;
		}
		PostDatabaseEvent ( album, COLLIGERE_EVT_DATABASE_ITEM_DELETE );
	}
	else
	{
		wxFAIL_MSG("Unimplemented");
	}
}

void col::AudioDatabaseHandler::PostDatabaseEvent ( const DatabaseItemBase *pItem,
												   wxInt32 commandType )
{
	// wxLogDebug ( "Posting database event (%d)", commandType );
	AudioDatabaseEvent e ( commandType, mId, pItem );
	mpLayoutParent->ProcessEvent ( e ); 
}

void col::AudioDatabaseHandler::PostDatabaseEvent ( wxInt32 commandType )
{
	// wxLogDebug ( "Posting database event (%d)", commandType );
	AudioDatabaseEvent e ( commandType, mId );
	mpLayoutParent->ProcessEvent ( e ); 
}

void col::AudioDatabaseHandler::AddItem ( const DatabaseItemBase &rItem )
{
	try
	{
		if ( const Artist *artist = dynamic_cast < const Artist * > ( &rItem ) )
		{
			if ( Artist *a = FindItem ( rItem.GetId() ) )
			{
				foreach ( const DatabaseItemBasePtr child, rItem.GetChildren() )
				{
					a->AddChild ( *child );
					PostDatabaseEvent ( a->GetLastChild(),
						COLLIGERE_EVT_DATABASE_ITEM_ADD );
				}
			}
			else
			{
				DatabaseItemBasePtr aptr ( new Artist ( artist->GetArtist(), 
					artist->GetCountry(), artist->GetHomepage() ) );
				mItemList.push_back ( aptr );
				aptr->GetState() = DatabaseItemState::Flag::Added;

				foreach ( const DatabaseItemBasePtr child, rItem.GetChildren() )
				{
					aptr->AddChild ( *child );
					PostDatabaseEvent ( aptr->GetLastChild(),
						COLLIGERE_EVT_DATABASE_ITEM_ADD );
				}
			}
		}
		else if ( dynamic_cast < const Album * > ( &rItem ) )
		{
			if ( Artist *a = FindItem ( rItem.GetParentId() ) )
			{
				a->AddChild ( rItem );
				PostDatabaseEvent ( a->GetLastChild(),
					COLLIGERE_EVT_DATABASE_ITEM_ADD );
			}
			else { wxFAIL_MSG ( "Artist not found!" ); }
		}
		else
		{
			wxFAIL_MSG ( "Unknown item!" );
		}
	}
	catch ( std::bad_cast &rE )
	{
		wxFAIL_MSG ( rE.what() );
	}
}

void col::AudioDatabaseHandler::UpdateItem ( const DatabaseItemBase &rItem )
{
	using namespace std;
	using namespace boost;

	if ( dynamic_cast < const Artist * > ( &rItem ) )
	{
		Artist *artist = FindItem ( rItem.GetId() );
		if ( rItem.CopyData ( artist ) > 0 )
		{
			artist->GetState() += DatabaseItemState::Flag::Changed;
		}
		foreach ( const DatabaseItemBasePtr album, rItem.GetChildren() )
		{
			UpdateItem ( *album );
		}
	}
	else if ( dynamic_cast < const Album * > ( &rItem ) )
	{
		Artist *artist = FindItem ( rItem.GetParentId() );
		Album *album = artist->FindChild ( rItem.GetId() );
		wxASSERT ( album );

		deque < wxUint32 > ignore_cols_album;

		ignore_cols_album.push_back ( Tables::Albums::TimeStampAdded );
		ignore_cols_album.push_back ( Tables::Albums::TimeStampUpdated );

		rItem.CopyData ( album, ignore_cols_album );
		
		// Tracks are currently treated differently than artists or albums (due to the
		// fact that the database part of the app isn't the best one around...) and 
		// every time an album is updated, all tracks are deleted and added again
		// from the new values. Note that even though album->ClearChildren() 
		// doesn't invoke any code that would clean up the actual SQL database, the 
		// old tracks are deleted in CommitItems() and thus they are not left to
		// pollute the database.
		album->ClearChildren();

		foreach ( DatabaseItemBasePtr track, rItem.GetChildren() )
		{
			album->AddChild ( *track );
		}
		wxASSERT ( rItem.GetChildCount() == album->GetChildCount() );

		artist->GetState() += DatabaseItemState::Flag::ChildChanged;

		PostDatabaseEvent ( album, COLLIGERE_EVT_DATABASE_ITEM_CHANGE );
	}
	else
	{
		wxFAIL_MSG ( "Unknown item type!" );
	}
}

void col::AudioDatabaseHandler::SetAlbumAsTrashed 
( wxUint32 artistId, wxUint32 albumId, bool trashed )
{
	Artist *artist = FindItem ( artistId );
	wxASSERT ( artist );
	Album *album = artist->FindChild ( albumId );
	wxASSERT ( album );

	artist->GetState() += DatabaseItemState::Flag::ChildChanged;
	album->SetAsTrashed ( trashed );
	
	PostDatabaseEvent ( album, trashed ? COLLIGERE_EVT_DATABASE_ITEM_DELETE 
		: COLLIGERE_EVT_DATABASE_ITEM_RESTORE );
}

void col::AudioDatabaseHandler::SetArtistAsTrashed ( wxUint32 artistId )
{
	try
	{
		Artist *a = FindItem ( artistId );
		wxASSERT(a); 

		a->GetState() += DatabaseItemState::Flag::ChildChanged;

		foreach ( DatabaseItemBasePtr item, a->GetChildren() )
		{
			Album &album = dynamic_cast < Album & > ( *item ); 
			album.SetAsTrashed();
			PostDatabaseEvent ( &album, COLLIGERE_EVT_DATABASE_ITEM_DELETE );
		}
		return;
	}
	catch ( std::bad_cast &rE )
	{
		wxFAIL_MSG ( rE.what() );
	}
	wxFAIL_MSG ( "Invalid artist ID!" );
}


void col::AudioDatabaseHandler::CommitChanges()
{
	try
	{
		if ( !IsTableStructureCreated() )
		{
			CreateTables();
			InsertMetadata();
		}

		if ( GetChangedItemCount() == 0 ) return;

		wxStopWatch sw;

		mDatabaseLayer.Begin();

		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_START );

		foreach ( DatabaseItemBasePtr artist, mItemList )
		{
			CommitItem ( *artist );
			wxASSERT ( artist->GetDatabaseId() != DatabaseItemBase::InvalidItemId );

			foreach ( DatabaseItemBasePtr a, artist->GetChildren() )
			{
				if ( a->GetState().IsNone() ) continue;
				try
				{
					Album &album = dynamic_cast < Album & > ( *a );

					PostDatabaseEvent ( &album, COLLIGERE_EVT_DATABASE_ITEM_COMMIT_START );

					if ( album.GetState().IsActive ( DatabaseItemState::Flag::Added ) )
					{
						album.SetTimestampAdded ( wxDateTime::Now().Format ( "%c" ) ); 
					}
					else if ( album.GetState().IsActive ( DatabaseItemState::Flag::Changed ) )
					{
						album.SetTimestampUpdated ( wxDateTime::Now().Format ( "%c") ); 
					}
					CommitItem ( album );

					wxASSERT ( album.GetDatabaseId() != DatabaseItemBase::InvalidItemId );

					RunQuery ( album.ToSqlDeleteChildrenQuery() );

					if ( !album.GetState().IsActive ( DatabaseItemState::Flag::Deleted ) )
					{
						foreach ( DatabaseItemBasePtr track, album.GetChildren() )
						{
							track->GetState() += DatabaseItemState::Flag::Added;
							track->SetDatabaseId ( DatabaseItemBase::InvalidItemId );
							CommitItem ( *track );
							wxASSERT ( track->GetDatabaseId() != DatabaseItemBase::InvalidItemId );
						}
					}
					PostDatabaseEvent ( &album, COLLIGERE_EVT_DATABASE_ITEM_COMMIT_END );
				}
				catch ( std::bad_cast &rE )
				{
					wxFAIL_MSG ( rE.what() );
				}
			}
		}
		UpdateMetadata();
		mDatabaseLayer.Commit();

		wxLogGeneric ( LogPerformance, _("Changes saved in %ldms."), sw.Time() );

		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_END );
	}
	catch ( wxSQLite3Exception &rE )
	{
		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_END );
		throw DatabaseError ( rE.GetMessage(), mInputFile ); 
	}

	// Clean-up deleted items from the item list.

	for ( DatabaseItemBaseDeque::iterator it = mItemList.begin(); 
		it != mItemList.end(); ++it )
	{
		if ( it->get()->GetState().IsActive ( DatabaseItemState::Flag::Deleted ) )
		{
			/*
			wxLogDebug ( "Permanently removed \"%s\" from the item list.",
				it->GetArtist().c_str() );
			*/
			it = mItemList.erase ( it );
			if ( it != mItemList.begin() ) --it;
		}
		else
		{
			for ( DatabaseItemBaseDeque::iterator child_it =
				it->get()->GetChildren().begin(); 
				child_it != it->get()->GetChildren().end(); ++child_it )
			{
				if ( child_it->get()->GetState().IsActive 
					( DatabaseItemState::Flag::Deleted ) )
				{
					/*
					wxLogDebug ( "Permanently removed \"%s - %s\" from the item list.",
						it->GetArtist().c_str(), child_it->get()->GetColumnValue
						( Tables::Albums::Title ).c_str() );
					*/
					child_it = it->get()->GetChildren().erase ( child_it );
					if ( child_it != it->get()->GetChildren().begin() ) --child_it;
				}
			}
		}
	}
}

wxUint32 col::AudioDatabaseHandler::GetChangedItemCount() const
{
	using namespace boost;
	using namespace std;

	if ( mItemList.empty() ) return 0;

	wxUint32 count = 0;

	foreach ( const DatabaseItemBasePtr artist, mItemList )
	{
		foreach ( const DatabaseItemBasePtr album, artist->GetChildren() )
		{
			if ( !album->GetState().IsNone() ) ++count;
		}
	}
	return count;
}

void col::AudioDatabaseHandler::QueryAllItems ( bool forceQueryDatabase )
{
	if ( mItemList.size() > 0 && !forceQueryDatabase )
	{
		wxLogDebug ( "Querying all items in the item list (%d item(s)).",
			mItemList.size() );

		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_START );

		foreach ( const DatabaseItemBasePtr artist, mItemList )
		{
			foreach ( const DatabaseItemBasePtr album, artist->GetChildren() )
			{
				if ( !album->GetState().IsActive ( DatabaseItemState::Flag::Deleted ) )
				{
					PostDatabaseEvent ( album.get(), COLLIGERE_EVT_DATABASE_ITEM_QUERIED );
				}
			}
		}
		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_END );
	}
	else
	{
		if ( !IsQueryable() ) return;
		
		wxLogDebug ( "Querying all items in the database." );

		PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_START );

		mItemList.clear();

		typedef DatabaseItemBase::ResetFlags rflags;

		try
		{
			const wxUint32 tid = GetTypeId();
			const DatabaseTable *artists_table = 
				DatabaseTableListHandler::Get().GetTable ( tid, "artists" );
			const DatabaseTable *albums_table = 
				DatabaseTableListHandler::Get().GetTable ( tid, "albums" );
			const DatabaseTable *tracks_table = 
				DatabaseTableListHandler::Get().GetTable ( tid, "tracks" );

			wxStopWatch sw;

			mDatabaseLayer.Begin();

			wxSQLite3ResultSet artists_set ( RunQueryWithResults 
				( artists_table->ToSqlSelectAllQuery() ) );
			
			while ( artists_set.NextRow() )
			{
				mItemList.push_back ( DatabaseItemBasePtr ( new Artist ) );
				*mItemList.back() << artists_set;
				
				wxSQLite3ResultSet albums_set ( RunQueryWithResults 
					( albums_table->ToSqlSelectAllQuery 
					( mItemList.back()->GetDatabaseId ( true ) ) ) );

				while ( albums_set.NextRow() )
				{
					Album album;
					album << albums_set;

					wxSQLite3ResultSet tracks_set ( RunQueryWithResults 
						( tracks_table->ToSqlSelectAllQuery 
						( album.GetDatabaseId ( true ) ) ) );

					while ( tracks_set.NextRow() )
					{
						Track track;
						track << tracks_set;
						album.AddChild ( track );
					}
					mItemList.back()->Reset ( rflags::State );
					mItemList.back()->AddChild ( album );

					PostDatabaseEvent ( mItemList.back()->GetLastChild(), 
						COLLIGERE_EVT_DATABASE_ITEM_QUERIED );
				}
			}
			mDatabaseLayer.Commit();

			wxLogDebug ( "%d artist(s) queried from the database.", mItemList.size() );
			wxLogGeneric ( LogPerformance, _("Querying took %ldms"), sw.Time() );
			PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_END );
		}
		catch ( wxSQLite3Exception &rE ) 
		{ 
			PostDatabaseEvent ( COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_END );
			throw DatabaseError ( rE.GetMessage(), mInputFile ); 
		}
	}
}

void col::AudioDatabaseHandler::OnDatabaseItemRequest ( AudioDatabaseEvent &rEvt )
{
	//wxLogDebug ( "Database item request received (%d).", rEvt.GetId() );

	const wxUint32 artist_id = rEvt.GetRequestedItemParentId(),
		album_id = rEvt.GetRequestedItemId();

	if ( artist_id != DatabaseItemBase::InvalidItemId && 
		album_id != DatabaseItemBase::InvalidItemId )
	{
		AudioDatabaseEvent evt ( COLLIGERE_EVT_DATABASE_ITEM_POST, mId, 
			FindItem ( artist_id )->FindChild ( album_id ) );	
		mpLayoutParent->ProcessEvent ( evt );
	}
	else if ( artist_id != DatabaseItemBase::InvalidItemId )
	{
		AudioDatabaseEvent evt ( COLLIGERE_EVT_DATABASE_ITEM_POST, 
			mId, FindItem ( artist_id ) );	
		mpLayoutParent->ProcessEvent ( evt );
	}
}

const col::Artist *col::AudioDatabaseHandler::FindItem ( wxUint32 artistId ) const
{
	return dynamic_cast < const Artist * > 
		( DatabaseHandlerBase::FindItem ( artistId ) );
}

col::Artist *col::AudioDatabaseHandler::FindItem ( wxUint32 artistId )
{
	return dynamic_cast < Artist * > 
		( DatabaseHandlerBase::FindItem ( artistId ) );
}

//////////////////////////////////////////////////////////
// Artist
//////////////////////////////////////////////////////////

col::DatabaseTable *col::Artist::mpsTableDescr = 0;

col::Artist::Artist()
{
	InitializeTableDescriptor();
}

col::Artist::Artist ( const wxString &rArtist, const wxString &rCountry, 
					 const wxString &rHomepage )
{
	InitializeTableDescriptor();

	SetColumnValue ( AudioDatabaseTables::Artists::Name, rArtist );
	SetColumnValue ( AudioDatabaseTables::Artists::Country, rCountry );
	SetColumnValue ( AudioDatabaseTables::Artists::Homepage, rHomepage );
}

bool col::Artist::operator == ( const Artist &rCmp ) const
{
	return rCmp.GetArtist() == GetArtist() && rCmp.GetCountry() == GetCountry(); 
}

col::Artist *col::Artist::Clone() const
{
	return new Artist ( *this );
}

void col::Artist::InitializeTableDescriptor()
{
	if ( !mpsTableDescr )
		mpsTableDescr = DatabaseTableListHandler::Get().GetTable
			( DatabaseTypeId::AudioDatabase, "artists" );
}

const wxString &col::Artist::GetArtist() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Artists::Name ); 
}

const wxString &col::Artist::GetCountry() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Artists::Country ); 
}

const wxString &col::Artist::GetHomepage() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Artists::Homepage ); 
}

const col::Album *col::Artist::FindChild ( wxUint32 childId ) const
{
	return dynamic_cast < const Album * > ( DatabaseItemBase::FindChild ( childId ) );
}

col::Album *col::Artist::FindChild ( wxUint32 childId )
{
	return dynamic_cast < Album * > ( DatabaseItemBase::FindChild ( childId ) );
}

void col::Artist::SetArtist ( const wxString &rArtist )
{
	SetColumnValue ( AudioDatabaseTables::Artists::Name, rArtist );
}

void col::Artist::SetCountry ( const wxString &rCountry )
{
	SetColumnValue ( AudioDatabaseTables::Artists::Country, rCountry );
}

void col::Artist::SetHomepage ( const wxString &rHomepage )
{
	SetColumnValue ( AudioDatabaseTables::Artists::Homepage, rHomepage );
}

const col::Album *col::Artist::GetLastChild() const 
{
	return dynamic_cast < const Album * > ( DatabaseItemBase::GetLastChild() );
}

const col::Album *col::Artist::GetFirstChild() const 
{
	return dynamic_cast < const Album * > ( DatabaseItemBase::GetFirstChild() );
}

//////////////////////////////////////////////////////////
// Album
//////////////////////////////////////////////////////////

col::DatabaseTable *col::Album::mpsTableDescr = 0;

col::Album::Album()
{
	InitializeTableDescriptor();
	SetAsTrashed ( false );
}

col::Album *col::Album::Clone() const
{
	return new Album ( *this );
}


void col::Album::InitializeTableDescriptor()
{
	if ( !mpsTableDescr )
		mpsTableDescr = DatabaseTableListHandler::Get().GetTable
			( DatabaseTypeId::AudioDatabase, "albums" );
}

const col::Track *col::Album::FindChild ( wxUint32 childId ) const
{
	return dynamic_cast < const Track * > ( DatabaseItemBase::FindChild ( childId ) );
}

col::Track *col::Album::FindChild ( wxUint32 childId )
{
	return dynamic_cast < Track * > ( DatabaseItemBase::FindChild ( childId ) );
}

const col::Track *col::Album::GetLastChild() const 
{
	return dynamic_cast < const Track * > ( DatabaseItemBase::GetLastChild() );
}

col::Album &col::Album::SetAlbum ( const wxString &rAlbum ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Albums::Title, rAlbum );
	return *this;
}

col::Album &col::Album::SetYear ( const wxString &rYear ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Albums::Year, rYear );
	return *this;
}

col::Album &col::Album::SetGenre ( const wxString &rGenre ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Albums::Genre, rGenre );
	return *this;
}

col::Album &col::Album::SetFormat ( const wxString &rType ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Albums::Format, rType );
	return *this;
}

col::Album &col::Album::SetCondition ( const wxString &rCondition ) 
{
	SetColumnValue ( AudioDatabaseTables::Albums::Condition, rCondition ); 
	return *this;
}

col::Album &col::Album::SetStatus ( const wxString &rStatus ) 
{
	SetColumnValue ( AudioDatabaseTables::Albums::Status, rStatus ); 
	return *this;
}

col::Album &col::Album::SetTimestampAdded ( const wxString &rTimestamp )
{
	SetColumnValue ( AudioDatabaseTables::Albums::TimeStampAdded, rTimestamp );
	return *this;
}

col::Album &col::Album::SetTimestampUpdated ( const wxString &rTimestamp )
{
	SetColumnValue ( AudioDatabaseTables::Albums::TimeStampUpdated, rTimestamp );
	return *this;
}

col::Album &col::Album::SetExtInfo ( const wxString &rInfo ) 
{
	SetColumnValue ( AudioDatabaseTables::Albums::ExtInfo, rInfo ); 
	return *this;
}

col::Album &col::Album::SetCatalogNum ( const wxString &rCatNum ) 
{
	SetColumnValue ( AudioDatabaseTables::Albums::CatalogNum, rCatNum );
	return *this; 
}

col::Album &col::Album::SetLabel( const wxString &rLabel ) 
{
	SetColumnValue ( AudioDatabaseTables::Albums::Label, rLabel ); 
	return *this;
}

col::Album &col::Album::SetRating ( const wxString &rRating )
{
	SetColumnValue ( AudioDatabaseTables::Albums::Rating, rRating );
	return *this;
}

col::Album &col::Album::SetTags ( const wxString &rTags )
{
	SetColumnValue ( AudioDatabaseTables::Albums::Tags, rTags );
	return *this;
}

col::Album &col::Album::SetPurchasePrice ( const wxString &rPrice )
{
	SetColumnValue ( AudioDatabaseTables::Albums::PurchasePrice, rPrice );
	return *this;
}

col::Album &col::Album::SetSellPrice ( const wxString &rPrice )
{
	SetColumnValue ( AudioDatabaseTables::Albums::SellPrice, rPrice );
	return *this;
}

col::Album &col::Album::SetAsTrashed ( bool trashed ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Albums::IsTrashed, trashed );
	return *this;
}

bool col::Album::IsTrashed() const 
{
	const wxString &val = GetColumnValue ( AudioDatabaseTables::Albums::IsTrashed );
	long b = -1;

	if ( !val.empty() )
	{
		if ( !val.ToLong ( &b ) )
		{
			wxFAIL;
			return false;
		}
		return b != 0;
	}
	return false;
}

const wxString &col::Album::GetAlbum() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Title ); 
}

const wxString &col::Album::GetYear() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Year ); 
}

const wxString &col::Album::GetGenre() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Genre ); 
}

const wxString &col::Album::GetFormat() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Format ); 
}

const wxString &col::Album::GetCondition() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Condition ); 
}

const wxString &col::Album::GetStatus() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::Status ); 
}

const wxString &col::Album::GetExtInfo() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Albums::ExtInfo ); 
}

const wxString &col::Album::GetTimestampAdded() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::TimeStampAdded );
}

const wxString &col::Album::GetTimestampUpdated() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::TimeStampUpdated );
}

const wxString &col::Album::GetLabel() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::Label );
}

const wxString &col::Album::GetCatalogNum() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::CatalogNum );
}

const wxString &col::Album::GetRating() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::Rating );
}

const wxString &col::Album::GetSellPrice() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::SellPrice );
}

const wxString &col::Album::GetPurchasePrice() const
{
	return GetColumnValue ( AudioDatabaseTables::Albums::PurchasePrice );
}

col::string_deque col::Album::GetTags() const
{
	const wxString &flat_tags = GetColumnValue ( AudioDatabaseTables::Albums::Tags );

	string_deque ret;

	wxStringTokenizer tkz ( flat_tags, "," );

	while ( tkz.HasMoreTokens() )
	{
		wxString t ( tkz.GetNextToken() );
		t.Trim().Trim ( false );
		ret.push_back (t);
	}
	return ret;
}

//////////////////////////////////////////////////////////
// Track
//////////////////////////////////////////////////////////

col::DatabaseTable *col::Track::mpsTableDescr = 0;

col::Track::Track()
{
	InitializeTableDescriptor();
}

col::Track::Track ( const wxString &rNum, const wxString &rTitle, const wxString &rLen )
{
	InitializeTableDescriptor();

	SetColumnValue ( AudioDatabaseTables::Tracks::Title, rTitle );
	SetColumnValue ( AudioDatabaseTables::Tracks::Length, rLen );
	SetColumnValue ( AudioDatabaseTables::Tracks::Num, rNum );
}

col::Track *col::Track::Clone() const
{
	return new Track ( *this );
}

void col::Track::InitializeTableDescriptor()
{
	if ( !mpsTableDescr )
		mpsTableDescr = DatabaseTableListHandler::Get().GetTable
			( DatabaseTypeId::AudioDatabase, "tracks" );
}

void col::Track::SetTitle ( const wxString &rTitle ) 
{ 
	SetColumnValue ( AudioDatabaseTables::Tracks::Title, rTitle );
}

void col::Track::SetLength ( const wxString &rLen ) 
{
	SetColumnValue ( AudioDatabaseTables::Tracks::Length, rLen );
}

void col::Track::SetNum ( const wxString &rNum ) 
{
	if ( !rNum.empty() )
	{
		long num = -1;
		rNum.ToLong ( &num );
		wxASSERT ( num != -1 );
		
		if ( num < 10 && rNum.at(0) != '0' )
		{
			SetColumnValue ( AudioDatabaseTables::Tracks::Num, "0" + rNum );
		}
		else
		{
			SetColumnValue ( AudioDatabaseTables::Tracks::Num, rNum );
		}
	}
}

const wxString &col::Track::GetTitle() const
{ 
	return GetColumnValue ( AudioDatabaseTables::Tracks::Title );
}

const wxString &col::Track::GetLength() const 
{ 
	return GetColumnValue ( AudioDatabaseTables::Tracks::Length );
}

const wxString &col::Track::GetNum() const 
{
	return GetColumnValue ( AudioDatabaseTables::Tracks::Num );
}

//////////////////////////////////////////////////////////
// ColligereAudioDatabaseEvent
//////////////////////////////////////////////////////////

col::AudioDatabaseEvent::AudioDatabaseEvent 
( wxEventType commandType, wxInt32 id,  const DatabaseItemBase *pItem ) 
: DatabaseEventBase ( commandType, id, pItem )
{
	m_propagationLevel = wxEVENT_PROPAGATE_MAX;
}

col::AudioDatabaseEvent::AudioDatabaseEvent ( wxEventType commandType, wxInt32 id ) : 
col::DatabaseEventBase ( commandType, id )
{
	m_propagationLevel = wxEVENT_PROPAGATE_MAX;
}
	
wxEvent *col::AudioDatabaseEvent::Clone() const 
{ 
	return ( new AudioDatabaseEvent ( *this ) ); 
}

const col::Artist *col::AudioDatabaseEvent::GetArtist() const 
{
	const DatabaseItemBase *item = GetItem();

	if ( const Artist *artist = dynamic_cast < const Artist * > ( item ) )
	{
		return artist;
	}
	else if ( const Album *album = dynamic_cast < const Album * > ( item ) )
	{
		return dynamic_cast < const Artist * > ( &item->GetParent() );
	}
	wxFAIL;
	return 0;
}

const col::Album *col::AudioDatabaseEvent::GetAlbum() const 
{ 
	wxASSERT ( GetItem() );
	return dynamic_cast < const Album * > ( GetItem() ); 
}